#define PrynInternal
#define PrynImplementation
#include <pryn.h>

#define PrynCheckFactory() \
	prynAssert (factory, NullArgument); \
	state = factory->pState;

#define PrynFactoryBody(BODY) PrynBaseBody (factory, { state = factory->pState; BODY })
#define PrynFactoryGetProperty(PropName, PropType, PropInitial, PropValue) PrynBaseGetProperty (, Factory, factory, PropName, PropType, PropInitial, { *output = PropValue; })
#define PrynFactorySetProperty(PropName, PropType, PropAssign) PrynBaseSetProperty (, Factory, factory, PropName, PropType, PropAssign)

PrynFactoryGetProperty (State, PrynState *, 0, state)
PrynFactoryGetProperty (Name, PrynString, nullString, factory->pCommonResource.pName)
PrynFactoryGetProperty (Description, PrynString, nullString, factory->pCommonResource.pDescription)
PrynFactoryGetProperty (Tags, PrynTagList *, 0, &factory->pCommonObject.pTags)

static PrynResult displayNameDefault (PrynComponent *component, PrynString *value, void *data)
{
	if (!component || !value) return PrynResultNullArgument;
	*value = component->pCommonResource.pName;
	return PrynResultSuccess;
}

static PrynResult clickDefault (PrynComponent *component, void *data)
{
	if (!component) return PrynResultNullArgument;
	return PrynResultDone;
}

static PrynResult pinDefault (PrynPin *pin, void *data) { return PrynResultDone; }

PrynExport (PrynResult, prynFactoryFind, (PrynFactory **output, PrynState *state, const PrynString *id))
{
	PrynStartFunctionHasState ();
	PrynFactory *factory;

	if (output) *output = 0;
	prynAssert (state && id, NullArgument);
	
	for (factory = 0; !prynStateIterateFactories (state, &factory); )
		if (!prynStringEqual (&factory->pId, id))
		{
			if (output) *output = factory;
			prynReturnSuccess ();
		}

	prynReturnDone ();
	PrynEndFunction ();
}

PrynExport (PrynResult, prynFactoryCreate, (PrynFactory **output, PrynLibrary *library, const PrynString *id))
{
	PrynStartFunction ();
	PrynFactory *factory;

	if (output) *output = 0;
	prynAssert (library, NullArgument);
	state = library->pState;
	prynAssert (id && id->pointer, NullArgument);
	
	if (prynFactoryFind (&factory, state, id))
	{
		prynCall (prynAllocateClearedObject (state, &factory));
		factory->pCommonObject.pType = PrynObjectTypeFactory;
		factory->pState = state;
		factory->pLibrary = library;
		prynStringDuplicate (&factory->pId, state, id);
		factory->pDisplayNameFunction = &displayNameDefault;
		factory->pClickFunction = &clickDefault;
		factory->pPinConnectedFunction = &pinDefault;
		factory->pPinDisconnectedFunction = &pinDefault;
		factory->pPinChangeValueFunction = &pinDefault;
		prynDListAppendP (state->pFactories, factory);
	}
	
	factory->pCommonObject.pMark = true;
	factory->pLibrary = library;
	if (output) *output = factory;
	
	PrynEndFunction ();
}

PrynExport (PrynResult, prynFactoryLoad, (PrynFactory *factory))
{
	PrynFactoryBody ({
		prynCall (prynLibraryLoad (factory->pLibrary));
		prynAssert (factory->pCreateFunction, Failure);
	});
}

PrynExport (PrynResult, prynFactoryDestroy, (PrynFactory *factory))
{
	PrynStartFunction ();
	PrynComponent *component, *componentNext;

	PrynCheckFactory ();

	for (component = factory->pComponents.first; component; component = componentNext)
	{
		componentNext = component->pNext;
		prynComponentDisassociate (component);
	}

	prynCommonObjectDestroy (&factory->pCommonObject, state);
	prynCommonResourceDestroy (&factory->pCommonResource, state);

	PrynDestroyCallback (factory->pCreateFunction);
	PrynDestroyCallback (factory->pDisplayNameFunction);
	PrynDestroyCallback (factory->pClickFunction);
	PrynDestroyCallback (factory->pPinConnectedFunction);
	PrynDestroyCallback (factory->pPinDisconnectedFunction);
	PrynDestroyCallback (factory->pPinChangeValueFunction);

	prynStringFree (&factory->pId, state);
	prynDListRemoveP (state->pFactories, factory);
	prynFree (state, factory);

	PrynEndFunction ();
}

PrynFactorySetProperty (Name, const PrynString *, { prynCommonResourceSetName (&factory->pCommonResource, value); })
PrynFactorySetProperty (Description, const PrynString *, { prynCommonResourceSetDescription (&factory->pCommonResource, value); })
PrynFactorySetProperty (Proprietary, void *, { factory->pProprietary = value; })
PrynFactorySetProperty (Mediator, PrynMediator *, { prynAssert (value->pLibrary->pState == factory->pState, BadArgument); factory->pMediator = value; })

PrynSetCallbackDefinition (, Factory, CreateFunction, self->pState, 0)
PrynSetCallbackDefinition (, Factory, DisplayNameFunction, self->pState, &displayNameDefault)
PrynSetCallbackDefinition (, Factory, ClickFunction, self->pState, &clickDefault)
PrynSetCallbackDefinition (, Factory, PinConnectedFunction, self->pState, &pinDefault)
PrynSetCallbackDefinition (, Factory, PinDisconnectedFunction, self->pState, &pinDefault)
PrynSetCallbackDefinition (, Factory, PinChangeValueFunction, self->pState, 0)